#import "checkers.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

piece_t chbGetPiece(CheckersBoard* board, CheckersPos* pos) {
	return board->board[pos->y / 2 * BOARD_W + pos->x];
}
void chbSetPiece(CheckersBoard* board, CheckersPos* pos, piece_t piece) {
	board->board[pos->y / 2 * BOARD_W + pos->x] = piece;
}

BOOL chbIsWithinBoard(CheckersBoard* notused, CheckersPos* pos) {
	return pos->x >= 0 && pos->y >= 0 && pos->x < BOARD_W && pos->y < BOARD_H;
}

void chbLeftForward(CheckersBoard* board, CheckersPos* pos) {
	pos->y--;
	if (board->iswhiteturn) pos->x++;
	else pos->x--;
}
void chbRightForward(CheckersBoard* board, CheckersPos* pos) {
	pos->y++;
	if (board->iswhiteturn) pos->x++;
	else pos->x--;
}

void chbInitBoard(CheckersBoard* board) {
	static piece_t empty_board[] = {
			1, 1, 1, 0, 0, -1, -1, -1,
			1, 1, 1, 0, 0, -1, -1, -1,
			1, 1, 1, 0, 0, -1, -1, -1,
			1, 1, 1, 0, 0, -1, -1, -1 };

	memcpy(board->board, empty_board, sizeof(piece_t) * BOARD_SQRS);
	board->mustjump = NO, board->iswhiteturn = YES, board->isjumpseq = NO;
	board->hashvalue = 0;
}

void chbInitPosition(CheckersBoard* notused, CheckersPos* pos, int sqr) {
	pos->y = sqr / (BOARD_W / 2);
	pos->x = sqr % (BOARD_W / 2);
}

void chbUnmakeMove(CheckersBoard* board, CheckersUnmove* unmove) {
	CheckersPos midsquare;
	CheckersMove* move = &unmove->completed_move;
	chbSetPiece(board, &move->from, unmove->moved);
	if (move->isjump) {
		midsquare.x = (move->to.x + move->from.x) / 2;
		midsquare.y = (move->to.y + move->from.y) / 2;
		chbSetPiece(board, &midsquare, unmove->captured);
	}
	chbSetPiece(board, &move->to, NO_PIECE);
	if (!board->isjumpseq)
		board->iswhiteturn = !board->iswhiteturn;
	board->isjumpseq = unmove->wasjumpseq;
	memcpy(&board->lastto, &unmove->lastto, sizeof(CheckersPos));
}
void chbMakeMove(CheckersBoard* board, CheckersMove* move,
		CheckersUnmove* unmove) {
	/*Assumes move is valid (which it should always be)*/
	BOOL iswhite;
	int last_row, i;
	CheckersPos midsquare, p;

	CheckersPos* from = &move->from, *to = &move->to;
	piece_t moving_piece = chbGetPiece(board, from);

	iswhite = moving_piece > 0;
	last_row = iswhite ? 7 : 0;

	/*Set up the unmove structure*/
	unmove->completed_move = *move;
	unmove->moved = moving_piece;
	unmove->wasjumpseq = board->isjumpseq;
	memcpy(&unmove->lastto, &board->lastto, sizeof(CheckersPos));

	chbSetPiece(board, from, NO_PIECE);

	if (to->x == last_row) {
		/*Crown the piece*/
		moving_piece = iswhite ? KING_PIECE : -KING_PIECE;
	}

//	printf("Making move %d %d to %d %d\n", from->y/2, from->x, to->y/2, to->x);

	if (move->isjump) {
		/*Logically in between the two coordinates*/
		midsquare.x = (to->x + from->x) / 2;
		midsquare.y = (to->y + from->y) / 2;

		//	printf("Jumped over square is %d %d\n",
		//		midsquare.y, midsquare.x);
		unmove->captured = chbGetPiece(board, &midsquare);

		chbSetPiece(board, &midsquare, NO_PIECE);

		memcpy(&board->lastto, &move->to, sizeof(CheckersPos));

		board->isjumpseq = abs(moving_piece) == KING_PIECE ?
				chbCanKingJumpFrom(board, to) : chbCanJumpFrom(board, to);
	} else board->isjumpseq = NO;

	chbSetPiece(board, to, moving_piece);
//	printf("mj=%d\n", (int)move->isjump);

	if (!board->isjumpseq)
		board->iswhiteturn = !board->iswhiteturn;

//	chbDisplayBoard(board);
//	chbDisplayBoardWithMove(board, move);
}

BOOL chbIsValidMove(CheckersBoard* board, CheckersMove* move) {
	CheckersMove moves[CHECKERS_MOVE_BUFFER];
	int movecnt, i;
	movecnt = chbGenerateValidMoves(board, moves);
	for (i = 0; i < movecnt; i++) {
        
        printf("DChecking move %d %d %d %d\n", 
               moves[i].from.x, moves[i].from.y,
               moves[i].to.x, moves[i].to.y);
		if (moves[i].from.x == move->from.x
				&& moves[i].to.x == move->to.x
				&& moves[i].from.y == move->from.y
				&& moves[i].to.y == move->to.y) {
			return YES;
		}
	}
	return NO;
}

BOOL chbCanJumpFrom(CheckersBoard* board, CheckersPos* from) {
	BOOL iswhite;
	int i;
	piece_t piece;
	CheckersPos p;

	for (i = 0; i < 2; i++) {
		p.x = from->x, p.y = from->y;

		if (i == 0) chbRightForward(board, &p);
		else chbLeftForward(board, &p);

		if (chbIsWithinBoard(board, &p)) {
			piece = chbGetPiece(board, &p);
			iswhite = (piece > 0);
			if (piece != NO_PIECE && iswhite != board->iswhiteturn) {

				if (i == 0)	chbRightForward(board, &p);
				else chbLeftForward(board, &p);

				if (chbIsWithinBoard(board, &p) && chbGetPiece(board, &p) == 0) {
					return YES;
				}
			}
		}
	}
	return NO;
}

BOOL chbCanKingJumpFrom(CheckersBoard* board, CheckersPos* from) {
	BOOL iswhite;
	int i;
	piece_t piece;
	CheckersPos p;

	for (i = 0; i < 2; i++) {
		p.x = from->x, p.y = from->y;

		if (i < 2)
			p.x++;
		else
			p.x--;
		if (i & 1)
			p.y++;
		else
			p.y--;

		if (chbIsWithinBoard(board, &p)) {
			piece = chbGetPiece(board, &p);
			iswhite = (piece > 0);

			if (piece != NO_PIECE && iswhite != board->iswhiteturn) {
				if (i < 2)
					p.x++;
				else
					p.x--;
				if (i & 1)
					p.y++;
				else
					p.y--;


				if (chbIsWithinBoard(board, &p) && chbGetPiece(board, &p) == 0)
					return YES;
			}
		}
	}
	return NO;
}

static void chbGenerateMove(CheckersBoard* board, CheckersMove* move, int x1,
		int y1, int x2, int y2, BOOL isjump) {
	move->from.x = x1, move->from.y = y1;
	move->to.x = x2, move->to.y = y2;
	move->isjump = isjump;
}

void chbGenerateValidMovesFrom(CheckersBoard* board, CheckersPos* from,
		CheckersMove* moves, int* movecnt) {
	BOOL iswhite;
	int i;
	piece_t piece;
	CheckersPos p;

	for (i = 0; i < 2; i++) {
		p.x = from->x, p.y = from->y;

		if (i == 0) chbRightForward(board, &p);
		else chbLeftForward(board, &p);

		if (chbIsWithinBoard(board, &p)) {
			piece = chbGetPiece(board, &p);
			iswhite = (piece > 0);
			//	printf("coord within board x = %d, y = %d, piece = %d\n",
			//			p.x, p.y, piece);
			if (!piece) {
				if (board->mustjump)
					continue;

				chbGenerateMove(board, &moves[*movecnt], from->x, from->y, p.x,
						p.y, NO);
				//printf("Generated move from %d %d to %d %d\n",
				//		from->x, from->y, p.x, p.y);
				(*movecnt)++;
			} else if (iswhite != board->iswhiteturn) {

				if (i == 0)
					chbRightForward(board, &p);
				else
					chbLeftForward(board, &p);

				if (chbIsWithinBoard(board,&p) && chbGetPiece(board, &p) == NO_PIECE) {
					/*The moment we noticed a jumping move, we back track all generated normal moves
					 * and start generating capturing moves */
					if (!board->mustjump) {
						*movecnt = 0;
						board->mustjump = YES;
					}
					chbGenerateMove(board, &moves[*movecnt], from->x, from->y,
							p.x, p.y, YES);
					(*movecnt)++;
				}
			}
		}
	}
}

void chbGenerateValidKingMovesFrom(CheckersBoard* board, CheckersPos* from,
		CheckersMove* moves, int* movecnt) {
	BOOL iswhite;
	int i;
	piece_t piece;
	CheckersPos p;

	for (i = 0; i < 4; i++) {
		p.x = from->x, p.y = from->y;

		if (i < 2)
			p.x++;
		else
			p.x--;
		if (i & 1)
			p.y++;
		else
			p.y--;

		if (chbIsWithinBoard(board, &p)) {
			piece = chbGetPiece(board, &p);
			iswhite = (piece > 0);

			//	printf("coord within board x = %d, y = %d, piece = %d\n",
			//			p.x, p.y, piece);
			if (!piece) {
				if (board->mustjump)
					continue;
				chbGenerateMove(board, &moves[*movecnt], from->x, from->y, p.x,
						p.y, NO);
				//printf("Generated move from %d %d to %d %d\n",
				//		from->x, from->y, p.x, p.y);
				(*movecnt)++;
			} else if (iswhite != board->iswhiteturn) {

				if (i < 2)
					p.x++;
				else
					p.x--;
				if (i & 1)
					p.y++;
				else
					p.y--;

				if (chbIsWithinBoard(board,
						&p) && chbGetPiece(board, &p) == NO_PIECE) {
				/*The moment we notice a jumping move, we back track all generated normal moves
				 * and start generating capturing moves */
				if (!board->mustjump) {
					*movecnt = 0;
					board->mustjump = YES;
				}
				chbGenerateMove(board, &moves[*movecnt], from->x, from->y,
						p.x, p.y, YES);
				(*movecnt)++;
			}
		}
	}
}
}

int chbGenerateValidMoves(CheckersBoard* board, CheckersMove* moves) {
	int movecnt, column;
	CheckersPos p;
	piece_t piece;
	BOOL iswhite;

	movecnt = 0;
	board->mustjump = board->isjumpseq;

	//Short circuit if there is a jump sequence
	if (board->isjumpseq){
		p.x = board->lastto.x, p.y = board->lastto.y;
		piece = chbGetPiece(board, &p);
		if (abs(piece) == KING_PIECE)
			chbGenerateValidKingMovesFrom(board, &p, moves, &movecnt);
		else
			chbGenerateValidMovesFrom(board, &p, moves, &movecnt);
		return movecnt;
	}

	for (column = 0; column < BOARD_H / 2; column++) {
		for (p.x = 0; p.x < BOARD_W; p.x++) {
			p.y = column * 2 + (p.x & 1);
			piece = chbGetPiece(board, &p);
			iswhite = (piece > 0);
			if (piece && iswhite == board->iswhiteturn) {
				if (abs(piece) == KING_PIECE)
					chbGenerateValidKingMovesFrom(board, &p, moves, &movecnt);
				else
					chbGenerateValidMovesFrom(board, &p, moves, &movecnt);
			}
		}
	}

	return movecnt;
}

void chbDisplayBoard(CheckersBoard* board) {
	CheckersPos p;
	piece_t piece;
	char c;
	printf("   0 1 2 3 \n");
	for (p.x = BOARD_W - 1; p.x >= 0; p.x--) {
		printf("%d: ", p.x);
		for (p.y = 0; p.y < BOARD_H; p.y++) {
			if (p.y % 2 == p.x % 2) {
				piece = chbGetPiece(board, &p);
				c = '-';
				if (piece > 0)
					c = 'X';
				else if (piece < 0)
					c = 'O';
				printf("%c", c);
			} else {
				printf(" ");
			}
		}
		printf("\n");
	}
}

void chbDisplayBoardWithMove(CheckersBoard* board, CheckersMove* move) {
	CheckersPos p, cent;
	piece_t piece;
	char c;
	printf("   0 1 2 3\n");
	for (p.x = BOARD_W - 1; p.x >= 0; p.x--) {
		printf("%d: ", p.x);
		for (p.y = 0; p.y < BOARD_H; p.y++) {
			if (p.y % 2 == p.x % 2) {
				cent.x = (move->from.x + move->to.x) / 2;
				cent.y = (move->from.y + move->to.y) / 2;
				if (move->from.x == p.x && move->from.y == p.y)
					c = 'F';
				else if (move->to.x == p.x && move->to.y == p.y)
					c = 'T';
				else if (p.x == cent.x && p.y == cent.y)
					c = 'C';
				else {
					piece = chbGetPiece(board, &p);
					c = '-';
					if (piece == 2) c = 'K';
					else if (piece == -2) c = '8';
					else if (piece > 0) c = 'X';
					else if (piece < 0)	c = 'O';
				}
				printf("%c", c);
			} else {
				printf(" ");
			}
		}
		printf("\n");
	}
}

BOOL chbIsGameOver(CheckersBoard* board, int* game_result) {
	CheckersMove moves[CHECKERS_MOVE_BUFFER];
	int movecnt;
	movecnt = chbGenerateValidMoves(board, moves);
	if (movecnt == 0) {
		*game_result = board->iswhiteturn ? -1 : 1;
		return YES;
	}
	return NO;
}
